package com.android.serialport;

import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;

import com.android.serialport.api.SerialPort;
import com.android.serialport.itf.DataListener;
import com.android.serialport.itf.OpenListener;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import androidx.annotation.NonNull;

/**
 * @author Administrator
 * @date 2021/1/28
 */
public class SerialPortDevice {

    private SerialPort mSerialPort;
    private final String mPath;
    private final int mBaudRate;
    private final int mStopBits;
    private final int mDataBits;
    private final int mParity;
    private final int mFlowCon;
    private final int mFlags;
    private OutputStream mOutputStream;
    private DataListener mDataListener;
    private ReceiveThread mReceiveThread;
    private HandlerThread mHandlerThread;
    private Handler mWriteHandler;

    public SerialPortDevice(String path, int baudRate, int stopBits, int dataBits,
                            int parity, int flowCon, int flags) {
        mPath = path;
        mBaudRate = baudRate;
        mStopBits = stopBits;
        mDataBits = dataBits;
        mParity = parity;
        mFlowCon = flowCon;
        mFlags = flags;
    }


    public void open(OpenListener openListener) {
        if (mSerialPort == null) {
            File file = new File(mPath);
            try {
                mSerialPort = new SerialPort(file, mBaudRate, mStopBits,
                        mDataBits, mParity, mFlowCon, mFlags);
                InputStream inputStream = mSerialPort.getInputStream();
                mOutputStream = mSerialPort.getOutputStream();
                startReceiveThread(inputStream);
                startWriteThread();
                if (openListener != null) {
                    openListener.openSucceed(file);
                }
            } catch (IOException e) {
                if (openListener != null) {
                    openListener.onFailed(file, Status.OPEN_FAIL);
                }
            } catch (SecurityException e) {
                if (openListener != null) {
                    openListener.onFailed(file, Status.NO_READ_WRITE_PERMISSION);
                }
            }
        }
    }

    public void setDataListener(DataListener listener) {
        mDataListener = listener;
    }


    private void startReceiveThread(InputStream inputStream) {
        mReceiveThread = new ReceiveThread(inputStream) {
            @Override
            public void onReceived(byte[] bytes) {
                if (mDataListener != null) {
                    mDataListener.receive(bytes);
                }
            }
        };
        mReceiveThread.start();
    }

    private void startWriteThread() {
        mHandlerThread = new HandlerThread("write-port-thread-" + mPath);
        mHandlerThread.start();
        mWriteHandler = new Handler(mHandlerThread.getLooper()) {
            @Override
            public void handleMessage(@NonNull Message msg) {
                byte[] sendBytes = (byte[]) msg.obj;
                if (null != mOutputStream && null != sendBytes && sendBytes.length > 0) {
                    try {
                        mOutputStream.write(sendBytes);
                        if (null != mDataListener) {
                            mDataListener.send(sendBytes);
                        }
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
        };
    }

    private void stopReadThread() {
        if (mReceiveThread != null) {
            mReceiveThread.close();
        }
    }

    private void stopWriteThread() {
        mWriteHandler = null;
        if (mHandlerThread != null) {
            mHandlerThread.interrupt();
            mHandlerThread.quit();
            mHandlerThread = null;
        }
    }

    public void close() {
        stopWriteThread();
        stopReadThread();
        if (mOutputStream != null) {
            try {
                mOutputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            mOutputStream = null;
        }
        if (mSerialPort != null) {
            mSerialPort.close();
        }
    }

    public void write(byte[] data) {
        if (mWriteHandler != null) {
            Message message = Message.obtain();
            message.obj = data;
            mWriteHandler.sendMessage(message);
        }
    }

}